﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

using WPF_Dialogs;
using WPF_Dialogs.Custom_Controls;

namespace WPF_Dialogs.Dialogs
{
    /// <summary>
    /// Interaction logic for FolderBrowseDialog.xaml
    /// </summary>
    public partial class FolderBrowseDialog : Window, IDialog
    {
        public new EDialogResult DialogResult { set; get; }
        public string SelectedPath { get; set; }
        public string TextForComputer { get; set; }
        public string TextForNewFolder { get; set; }
        public string TextForRename { get; set; }
        public string TextForDelete { get; set; }

        private bool lastValueOfEmptyFolders = true;
        private bool lastValueOfHiddenFolders = true;

        /// <summary>
        /// The Root-Path of the FolderBrowseDialog; DEFAULT: Desktop
        /// </summary>
        public string InitialPath { get; set; }

        /// <summary>
        /// Gets or sets how deep the tree should be shown; 0 = all
        /// </summary>
        public int TreeDepth { get; set; }

        private List<string> specialFolders = new List<string>();

        public enum ESpecialFolders : int
        {
            Desktop = 0,
            Documents = 1,
            Pictures = 2,
            Computer = 4,
            Music = 5,
            StartMenu = 6,
            ProgramFiles = 7
        }

        public FolderBrowseDialog()
        {
            InitializeComponent();

            #region Set the SpecialFolders
            for (int i = 0; i <= 8; i++)
                specialFolders.Add("");
            specialFolders[(int)ESpecialFolders.Desktop]= Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
            specialFolders[(int)ESpecialFolders.Documents] = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            specialFolders[(int)ESpecialFolders.Pictures] = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
            specialFolders[(int)ESpecialFolders.Computer] = Environment.GetFolderPath(Environment.SpecialFolder.MyComputer);
            specialFolders[(int)ESpecialFolders.Music] = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic);
            specialFolders[(int)ESpecialFolders.StartMenu] = Environment.GetFolderPath(Environment.SpecialFolder.StartMenu);
            specialFolders[(int)ESpecialFolders.ProgramFiles] = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
            #endregion

            this.InitialPath = SpecialFolders(ESpecialFolders.Desktop);
            SelectedPath = "";
            TreeDepth = 0;

            this.TextForComputer = "Computer";
            this.TextForNewFolder = "New Folder";
            this.TextForRename = "Rename";
            this.TextForDelete = "Delete";

            this.buttonNewFolder.Height = 23;
            this.buttonNewFolder.Width = 75;    
        }

        #region private functions
        private void DialogButton_clicked(object sender, RoutedEventArgs e)
        {
            DialogButton o = (DialogButton)sender;
            this.DialogResult = o.DialogResult;
            if (this.DialogResult == EDialogResult.OK && this.treeViewFolders.SelectedItem != null)
            {
                try
                {
                    this.SelectedPath = ((TreeViewItem)this.treeViewFolders.SelectedItem).ToolTip.ToString();
                }
                catch (Exception ex) //Throwing exception if "Computer" or sth.else was choosen (sth. without a path), the developer has to handle this.
                {
                    Exception own = new Exception("No valid Folder choosen.");
                    own.Source = "WPF_Dialogs.dll:FolderBrowseDialog:Throwing exception if \"Computer\" or sth.else was choosen (sth. without a path), the developer has to handle this.";
                    throw own;
                }
            }
            this.Close();
        }

        private void loadFolders()
        {
            loadFolders ("");
        }

        private void loadFolders(string initialPath)
        {
            this.treeViewFolders.Items.Clear();
            if (initialPath != "") //
                this.InitialPath = initialPath;
            if (!Directory.Exists(this.InitialPath))
            {
                Exception e = new Exception("The Initial Path doesn't exist.");
                throw e;
            }
            if (this.SelectedPath != "")
            {
                if (!Directory.Exists(this.SelectedPath))
                {
                    Exception e = new Exception("The Selected Path doesn't exist.");
                    throw e;
                }
                if (!selectedPathIsInInitialpath())
                {
                    Exception e = new Exception("The Selected Path isn't part of the Initial Path.");
                    throw e;
                }
            }
            treeViewFolders.Items.Add(loadDirectories(this.InitialPath));
            ((TreeViewItem)treeViewFolders.Items[0]).IsExpanded = true;
            selectPath();
        }

        private TreeViewItem loadDirectories(string path)
        {
            TreeViewItem tvi = createTreeVieItem(path);
            tvi.Items.Clear();
            if (this.InitialPath == SpecialFolders(ESpecialFolders.Desktop))
            {
                loadDesktop(tvi);
            }
            string [] folders = Directory.GetDirectories(path);
            for (int i = 0; i < folders.Length; i++)
            {
                try
                {
                    if (this.checkBoxEmptyFolders.IsChecked != true)
                    {
                        if(!folderIsEmpty(folders[i]))
                            tvi.Items.Add(createTreeVieItem(folders[i]));
                    }
                    else if (this.checkBoxHiddenFolders.IsChecked != true)
                    {
                        if (!folderIsHidden(folders[i]))
                            tvi.Items.Add(createTreeVieItem(folders[i]));
                    }
                    else
                        tvi.Items.Add(createTreeVieItem(folders[i]));
                }
                catch (Exception e) //If there are no rights for the folder, bad solution but best now
                { }
            }
            return tvi;
        }

        private TreeViewItem createTreeVieItem(string path)
        {
            TreeViewItem tvi = new TreeViewItem();
            string header = path.Remove(0, path.LastIndexOf("\\") + 1);
            if (header == "") //The Path is a Drive (e.g. C\, after removing \ -> "")
            {
                header = path.Remove(path.Length - 1);
                tvi.ContextMenu = createContextMenu(true);
            }
            else
                tvi.ContextMenu = createContextMenu();
            tvi.Header = header;
            tvi.ToolTip = path;
            tvi.MouseRightButtonDown += new MouseButtonEventHandler(treeViewItems_MouseRightButtonDown);
            if (this.TreeDepth == 0) //Without depth
            {
                if (path != "")
                {
                    if (Directory.GetDirectories(path).Length > 0)
                    {
                        tvi.Items.Add(new TreeViewItem());
                        tvi.Expanded += new RoutedEventHandler(tvi_Expanded);
                        tvi.Selected += new RoutedEventHandler(tvi_Selected);   
                    }
                }
            }
            else //with depth
            {
                if (path != "" && getDepth(path) < this.TreeDepth)
                {
                    if (Directory.GetDirectories(path).Length > 0)
                    {
                        tvi.Items.Add(new TreeViewItem());
                        tvi.Expanded += new RoutedEventHandler(tvi_Expanded);
                        tvi.Selected += new RoutedEventHandler(tvi_Selected);
                    }
                }
                else
                {
                    tvi.Expanded += new RoutedEventHandler(doNothing_Event);
                    tvi.Selected += new RoutedEventHandler(doNothing_Event);
                }
            }
            return tvi;
        }

        private void doNothing_Event(object sender, RoutedEventArgs e)
        {
            e.Handled = true;
        }

        private void tvi_Expanded(object sender, RoutedEventArgs e)
        {
            var tvi = e.OriginalSource as TreeViewItem;
            if (tvi.Items.Count <= 1 && tvi.IsExpanded)
            {
                tvi.Items.Clear();
                string[] folders = Directory.GetDirectories(tvi.ToolTip.ToString());
                for (int i = 0; i < folders.Length; i++)
                {
                    try
                    {
                        if (this.checkBoxEmptyFolders.IsChecked != true)
                        {
                            if (!folderIsEmpty(folders[i]))
                                tvi.Items.Add(createTreeVieItem(folders[i]));
                        }
                        else if (this.checkBoxHiddenFolders.IsChecked != true)
                        {
                            if (!folderIsHidden(folders[i]))
                                tvi.Items.Add(createTreeVieItem(folders[i]));
                        }
                        else
                            tvi.Items.Add(createTreeVieItem(folders[i]));
                    }
                    catch (Exception ex) //If there are no rights for the folder, bad solution but best now
                    { }
                }
            }
            e.Handled = true;
        }

        private void tvi_Selected(object sender, RoutedEventArgs e)
        {
            var tvi = e.OriginalSource as TreeViewItem;
            if(tvi.Items.Count <= 1 && !tvi.IsExpanded)
                tvi.IsExpanded = true;
            e.Handled = true;
        }

        private void computer_Expanded(object sender, RoutedEventArgs e)
        {
            var tvi = getTreeViewItem((TreeViewItem)this.treeViewFolders.Items[0], "Computer");
            if (tvi.Items.Count <= 1 && tvi.IsExpanded)
            {
                tvi.Items.Clear();
                string[] folders = Directory.GetLogicalDrives();
                for (int i = 0; i < folders.Length; i++)
                {
                    try
                    {
                        if (this.checkBoxEmptyFolders.IsChecked != true)
                        {
                            if (!folderIsEmpty(folders[i]))
                                tvi.Items.Add(createTreeVieItem(folders[i]));
                        }
                        else if (this.checkBoxHiddenFolders.IsChecked != true)
                        {
                            if (!folderIsHidden(folders[i]))
                                tvi.Items.Add(createTreeVieItem(folders[i]));
                        }
                        else
                            tvi.Items.Add(createTreeVieItem(folders[i]));
                    }
                    catch (Exception ex) //The Drive is not ready, bad solution but best now
                    { }
                }
            }
            e.Handled = true;
        }

        private void loadDesktop(TreeViewItem tvi)
        {
            tvi.Header = SpecialFolders(ESpecialFolders.Desktop).Remove(0, SpecialFolders(ESpecialFolders.Desktop).LastIndexOf("\\") + 1);
            tvi.ToolTip = SpecialFolders(ESpecialFolders.Desktop);
            tvi.Items.Add(createTreeVieItem(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)));
            TreeViewItem computer = new TreeViewItem();
            computer.Header = this.TextForComputer;
            computer.Expanded += new RoutedEventHandler(computer_Expanded);
            computer.Selected += new RoutedEventHandler(tvi_Selected);
            computer.Items.Add("");
            tvi.Items.Add(computer);
        }

        private new bool? ShowDialog()
        {
            return base.ShowDialog();
        }

        private bool selectedPathIsInInitialpath()
        {
            if (this.InitialPath != SpecialFolders(ESpecialFolders.Desktop))
            {
                string[] initial = this.InitialPath.Split('\\');
                string[] selected = this.SelectedPath.Split('\\');
                for (int i = 0; i < initial.Length; i++)
                {
                    if (selected[i] != initial[i])
                        return false;
                }
            }
            return true;
        }

        private void selectPath()
        {
            if (this.SelectedPath != "" && this.SelectedPath != this.InitialPath)
            {
                string[] selected = this.SelectedPath.Split('\\');
                TreeViewItem actual = (TreeViewItem)this.treeViewFolders.Items[0];
                int startindex = 0; //if desktop
                if (this.InitialPath != SpecialFolders(ESpecialFolders.Desktop))
                {
                    string[] initial = this.InitialPath.Split('\\');
                    startindex = selected.Length - initial.Length;
                    startindex += 2; //to correct the substraction
                }
                else
                {
                    actual = getTreeViewItem(actual, "Computer");
                    actual.IsSelected = true;
                    actual.IsExpanded = true;
                }
                for (int i = startindex; i < selected.Length; i++)
                {
                    actual = getTreeViewItem(actual, selected[i]);
                    actual.IsSelected = true;
                    actual.IsExpanded = true;
                }
                this.buttonNewFolder.IsEnabled = true;
            }
            else if (this.SelectedPath == this.InitialPath)
            {
                TreeViewItem actual = (TreeViewItem)this.treeViewFolders.Items[0];
                actual.IsSelected = true;
                actual.IsExpanded = true;
            }
        }

        private TreeViewItem getTreeViewItem(TreeViewItem actual, string subItemName)
        {
            for (int i = 0; i < actual.Items.Count; i++)
            {
                if (((TreeViewItem)actual.Items[i]).Header.ToString() == subItemName)
                    return (TreeViewItem)actual.Items[i];
            }
            return null;
        }

        private int getDepth(string path)
        {
            int depth = gND(path);
            if (this.InitialPath == SpecialFolders(ESpecialFolders.Desktop))
            {
                if (path.Contains(SpecialFolders(ESpecialFolders.Desktop))) //Desktop->
                    depth -= gND(SpecialFolders(ESpecialFolders.Desktop));
                else if (path.Contains(SpecialFolders(ESpecialFolders.Documents))) //Desktop->Documents->
                {
                    depth -= gND(SpecialFolders(ESpecialFolders.Documents));
                    if (path == SpecialFolders(ESpecialFolders.Documents)) // + Desktop
                        depth += 1;
                    else // + Desktop + Documents
                        depth += 2;
                }
                else if (path.Length == 3) // all drives: C:\, D:\, etc..
                    depth = 3;
                else
                    depth -= 3; // Desktop->Computer->Drive->
            }
            else
            {
                depth -= gND(this.InitialPath);
                depth += 1; // + the Root of TreeView
            }
            return depth;
        }

        private void treeViewFolders_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            try
            {
                this.SelectedPath = ((TreeViewItem)this.treeViewFolders.SelectedItem).ToolTip.ToString();
                this.buttonNewFolder.IsEnabled = true;
            }
            catch (Exception ex)
            {
                this.buttonNewFolder.IsEnabled = false;
            }
            e.Handled = true;
        }

        private void buttonNewFolder_Click(object sender, RoutedEventArgs e)
        {
            NewFolderDialog nf = new NewFolderDialog(this.SelectedPath);
            if (nf.showDialog() == EDialogResult.OK)
            {
                reloadFolder(SelectedPath, nf.FolderName);
            }
            e.Handled = true;
        }

        private void reloadFolder(string path, string newFolder)
        {
            this.treeViewFolders.Items.Clear();
            if (path == this.InitialPath)
                loadFolders();
            else
            {
                string[] selected = this.SelectedPath.Split('\\');
                TreeViewItem actual = (TreeViewItem)this.treeViewFolders.Items[0];
                int startindex = 0; //if desktop
                if (this.InitialPath != SpecialFolders(ESpecialFolders.Desktop))
                {
                    string[] initial = this.InitialPath.Split('\\');
                    startindex = selected.Length - initial.Length;
                    startindex += 2; //to correct the substraction
                }
                else
                {
                    actual = getTreeViewItem(actual, "Computer");
                }
                for (int i = startindex; i < selected.Length; i++)
                {
                    actual = getTreeViewItem(actual, selected[i]);
                }
                actual.Items.Clear();
                actual.IsSelected = true;
                actual.IsExpanded = false;//for actiating the expanded-event
                actual.IsExpanded = true;
                this.buttonNewFolder.IsEnabled = true;
            }
            if(this.SelectedPath != newFolder)
                this.SelectedPath = path + "\\" + newFolder;
            selectPath();
        }

        private bool folderIsEmpty(string path)
        {
            if (Directory.GetDirectories(path).Length > 0)
                return false;
            if (Directory.GetFiles(path).Length > 0)
                return false;
            return true;
        }

        /// <summary>
        /// getNativeDepth (gND)
        /// </summary>
        /// <param name="path">Path</param>
        /// <returns>the depth of the path in the filesystem</returns>
        private int gND(string path)
        {
            return path.Split('\\').Length;
        }

        private void checkBoxEmptyFolders_Checked(object sender, RoutedEventArgs e)
        {
            if (lastValueOfEmptyFolders != true)
                checkedChanged();
            lastValueOfEmptyFolders = true;
            e.Handled = true;
        }

        private void checkedChanged()
        {
            this.treeViewFolders.Items.Clear();
            reloadFolder(this.InitialPath, this.SelectedPath);
        }

        private void checkBoxEmptyFolders_Unchecked(object sender, RoutedEventArgs e)
        {
            if (lastValueOfEmptyFolders != false)
                checkedChanged();
            lastValueOfEmptyFolders = false;
            e.Handled = true;
        }

        private void checkBoxHiddenFolders_Checked(object sender, RoutedEventArgs e)
        {
            if (lastValueOfHiddenFolders != true)
                checkedChanged();
            lastValueOfHiddenFolders = true;
            e.Handled = true;
        }

        private void checkBoxHiddenFolders_Unchecked(object sender, RoutedEventArgs e)
        {
            if (lastValueOfHiddenFolders != false)
                checkedChanged();
            lastValueOfHiddenFolders = false;
            e.Handled = true;
        }

        private bool folderIsHidden(string path)
        {
            DirectoryInfo di = new DirectoryInfo(path);
            if (di.Attributes.ToString().Contains(FileAttributes.Hidden.ToString()))
                return true;
            return false;
        }
        private ContextMenu createContextMenu()
        {
            return createContextMenu (false);
        }
        private ContextMenu createContextMenu(bool drive)
        {
            ContextMenu cm = new ContextMenu();
            MenuItem mi = new MenuItem();
            mi.Header = this.TextForNewFolder;
            mi.Click += new RoutedEventHandler(buttonNewFolder_Click);
            cm.Items.Add(mi);
            if (!drive)
            {
                mi = new MenuItem();
                mi.Header = this.TextForRename;
                mi.Click += new RoutedEventHandler(rename_Click);
                cm.Items.Add(mi);
                mi = new MenuItem();
                mi.Header = this.TextForDelete;
                mi.Click += new RoutedEventHandler(delete_Click);
                cm.Items.Add(mi);
            }
            return cm;
        }

        private void treeViewItems_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            TreeViewItem tvi = (TreeViewItem)sender;
            tvi.IsSelected = true;
            e.Handled = true;
        }

        private void rename_Click(object sender, RoutedEventArgs e)
        {
            TreeViewItem tvi = (TreeViewItem)this.treeViewFolders.SelectedItem;
            RenamerDialog r = new RenamerDialog(tvi.ToolTip.ToString());
            if (r.showDialog() == EDialogResult.OK)
            {
                this.SelectedPath = Directory.GetParent(SelectedPath).FullName;
                reloadFolder(this.SelectedPath, r.FolderName);
            }
            e.Handled = true;
        }

        private void delete_Click(object sender, RoutedEventArgs e)
        {
            TreeViewItem tvi = (TreeViewItem)this.treeViewFolders.SelectedItem;
            DeleteDialog r = new DeleteDialog(tvi.ToolTip.ToString()); 
            if (r.showDialog() == EDialogResult.OK)
            {
                try
                {
                    this.SelectedPath = Directory.GetParent(SelectedPath).FullName;
                    if (this.SelectedPath == this.InitialPath)                        
                        loadFolders();
                    else
                        reloadFolder(Directory.GetParent(SelectedPath).FullName, SelectedPath);
                }
                catch (IOException ex)
                {
                    throw ex;
                }
            }
            e.Handled = true;
        }
        #endregion

        #region public functions
        public string SpecialFolders(ESpecialFolders folder)
        {
            return this.specialFolders[(int)folder];
        }

        /// <summary>
        /// Shows the dialog. Can be used as if the function has no returntype (see samples)
        /// </summary>
        /// <returns></returns>
        public EDialogResult showDialog()
        {
            return showDialog("");
        }

        /// <summary>
        /// Shows the dialog. Can be used as if the function has no returntype (see samples)
        /// </summary>
        /// <returns></returns>
        public EDialogResult showDialog(string initialPath)
        {
            loadFolders(initialPath);
            this.treeViewFolders.Focus();
            this.ShowDialog();
            return this.DialogResult;
        }
        #endregion

        #region public properties
        /// <summary>
        /// Sets the content of the button new folder.
        /// </summary>
        /// <value>The content of the button new folder.</value>
        public object ButtonNewFolderContent
        {
            set
            {
                this.buttonNewFolder.Content = value;
            }
        }

        /// <summary>
        /// Sets the content of the button OK.
        /// </summary>
        /// <value>The content of the button OK.</value>
        public object ButtonOKContent
        {
            set
            {
                this.buttonOK.Content = value;
            }
        }

        /// <summary>
        /// Sets the content of the button cancel.
        /// </summary>
        /// <value>The content of the button cancel.</value>
        public object ButtonCancelContent
        {
            set
            {
                this.buttonCancel.Content = value;
            }
        }

        /// <summary>
        /// Sets the content of the check box for empty folders.
        /// </summary>
        /// <value>The content of the check box empty folders.</value>
        public object CheckBoxEmptyFoldersContent
        {
            set
            {
                this.checkBoxEmptyFolders.Content = value;
            }
        }

        /// <summary>
        /// Gets or sets the show of empty folders.
        /// </summary>
        /// <value>The show empty folders.</value>
        public bool? ShowEmptyFolders
        {
            get
            {
                return this.checkBoxEmptyFolders.IsChecked;
            }
            set
            {
                this.checkBoxEmptyFolders.IsChecked = value;
            }
        }

        /// <summary>
        /// Sets the content of the check box for hidden folders.
        /// </summary>
        /// <value>The content of the check box hidden folders.</value>
        public object CheckBoxHiddenFoldersContent
        {
            set
            {
                this.checkBoxHiddenFolders.Content = value;
            }
        }


        /// <summary>
        /// Gets or sets the show of hidden folders.
        /// </summary>
        /// <value>The show hidden folders.</value>
        public bool? ShowHiddenFolders
        {
            get
            {
                return this.checkBoxHiddenFolders.IsChecked;
            }
            set
            {
                this.checkBoxHiddenFolders.IsChecked = value;
            }
        }
        #endregion
    }
}